const shell = require("shelljs")
const repl = require("repl")
const $console = require("Console")
const path = require("path")
const sll = require("single-line-log").stdout
const fs = require("fs")
const qiniu = require("qiniu")
const rp = require("request-promise")
const package = require("./package")
const p = function({ fn, target, nodejs = true }) {
  return (...args) => {
    // eslint-disable-next-line no-undef
    return new Promise((resolve, reject) => {
      args.push((err, ...data) => {
        if (nodejs === false) {
          const result = [err, ...data]
          if (result.length > 1) {
            return resolve.call(this, [err, ...data])
          } else {
            return resolve.apply(this, [err, ...data])
          }
        } else if (err === null || err === undefined) {
          if (data.length > 1) {
            return resolve.call(this, data)
          } else {
            return resolve.apply(this, data)
          }
        } else {
          return reject(err)
        }
      })
      if (target) fn.apply(target, args)
      else fn.apply(null, args)
    })
  }
}
const fs_stat_ = p({
  fn: fs.stat,
  target: fs
})
const fs_readdir = p({
  fn: fs.readdir,
  target: fs
})
const fs_stat = async function(dir) {
  try {
    await fs_stat_(dir)
    return true
  } catch (e) {
    return false
  }
}
const fs_writeFile = p({
  fn: fs.writeFile,
  target: fs
})
const baseDirPro = package.base.baseDirPro
const baseDirTest = package.base.baseDirTest
const baseUriPro = package.base.baseUriPro
const pc = package.base.pc
const mobile = package.base.mobile
const qiniuAccessKey = "VcU4tLd0Y2pk_h_2RjE5YvnKgem29QAJyeBqEFyq"
const qiniuSecretKey = "enBmm7jgVVz7VmHG_Jyt-1Q3yjGs7tIlyK-4Pl7u"
let startTime = 0
let silent = false
let runing = false
let runingCode = ["^_^", "-_^", "-_-", "^_-", "^_^"]
let runingIndex = 0
setInterval(function() {
  if (runing === true && silent === true) {
    sll(runingCode[runingIndex++])
    if (runingIndex > runingCode.length) {
      runingIndex = 0
    }
  }
}, 300)
const runingChange = vl => {
  runing = vl
  sll("")
}
const bill = s => (s < 10 ? `0${s}` : `${s}`)
const format = now => {
  return `${now.getFullYear()}${bill(now.getMonth() + 1)}${bill(
    now.getDate()
  )}${bill(now.getHours())}${bill(now.getMinutes())}${bill(now.getSeconds())}`
}
const execP = p({
  fn: shell.exec,
  target: shell,
  nodejs: false
})
const exec = async cmd => {
  runingChange(true)
  const pro = await execP(cmd, {
    silent
  })
  runingChange(false)
  sll("")
  console.log()
  return pro
}
const cp = async function(dir1, dir2) {
  $console.success(`${dir1}->${dir2} 开始复制`)
  runingChange(true)
  await shell.cp("-Rf", dir1, dir2)
  runingChange(false)
  sll("")
  $console.success(`${dir1}->${dir2} 复制完毕`)
}
const mv = async function(dir1, dir2) {
  await shell.mv("-nf", dir1, dir2)
}
const rm = async function(dir1) {
  $console.warn(`删除:${dir1}`)
  await shell.rm("-rf", dir1)
}
const mkdir = async function(dir) {
  shell.mkdir("-p", dir)
}
const baseDir = shell.pwd().stdout

const nowDir = async function() {
  $console.warn(`工作目录变更为->${shell.pwd().stdout}`)
}
const returnBaseUri = async function() {
  shell.cd(baseDir)
  await nowDir()
}
const gotoProject = async function(mode) {
  shell.cd(path.join(baseDir, "node", mode))
  await nowDir()
}
const install = async function(mode, lib) {
  await gotoProject(mode)
  await exec(`yarn add ${lib}`)
}
const uninstall = async function(mode, lib) {
  await gotoProject(mode)
  await exec(`yarn remove ${lib}`)
}
const baseInstall = async function(mode, pc) {
  $console.warn(`准备安装${mode}项目【依赖】`)
  await rm(path.join(baseDir, "node", mode, "node_modules", "element-ui"))
  await gotoProject(mode)
  await exec("yarn")
  if (pc === true) {
    await exec("yarn add mk-element-ui")
    await mv(
      path.join(baseDir, "node", mode, "node_modules", "mk-element-ui"),
      path.join(baseDir, "node", mode, "node_modules", "element-ui")
    )
  }
  $console.success(`${mode}基本【依赖】安装完成`)
}

// 七牛CDN
const mac = new qiniu.auth.digest.Mac(qiniuAccessKey, qiniuSecretKey)
const cdnManager = new qiniu.cdn.CdnManager(mac)
const fetchUris = p({
  fn: cdnManager.prefetchUrls,
  target: cdnManager
})
const refreshUris = p({
  fn: cdnManager.refreshUrls,
  target: cdnManager
})
const fetch = async function(mode, uri) {
  //CDN预取
  $console.warn(`准备预取七牛CDN,每日名额:100,超过后失败`)
  const res = []
  const jss = await fs_readdir(
    path.join(baseDir, "node", mode, "dist", "static", "js")
  )
  jss.forEach(item => {
    res.push(`${uri}js/${item}`)
  })
  const csss = await fs_readdir(
    path.join(baseDir, "node", mode, "dist", "static", "css")
  )
  csss.forEach(item => {
    res.push(`${uri}css/${item}`)
  })
  const dlls = await fs_readdir(
    path.join(baseDir, "node", mode, "dist", "static", "dll")
  )
  dlls.forEach(item => {
    res.push(`${uri}dll/${item}`)
  })
  $console.warn(`计划预取${res.length}个文件`)
  const [respBody, respInfo] = await fetchUris(res)
  respInfo.data = respInfo.data.toString()
  if (respInfo.statusCode == 200) {
    const jsonBody = JSON.parse(respBody.toString())
    $console.warn(`
-----*完毕*-----
返回状态码：${jsonBody.code}(${jsonBody.error})
错误地址有：${JSON.stringify(jsonBody.invalidUrls)}
错误路径有：${JSON.stringify(jsonBody.invalidDirs)}
限额：${jsonBody.surplusDay}/${jsonBody.quotaDay}
    `)
  } else {
    $console.error(
      `七牛预取失败,因为返回状态码是${
        respInfo.statusCode
      },具体错误信息:${JSON.stringify(respInfo)}`
    )
  }
}
const refresh = async function(mode, uri) {
  //CDN预取
  $console.warn(`准备刷新七牛CDN,每日名额:500,超过后失败`)
  const res = []
  const jss = await fs_readdir(
    path.join(baseDir, "node", mode, "dist", "static", "js")
  )
  jss.forEach(item => {
    res.push(`${uri}js/${item}`)
  })
  const csss = await fs_readdir(
    path.join(baseDir, "node", mode, "dist", "static", "css")
  )
  csss.forEach(item => {
    res.push(`${uri}css/${item}`)
  })
  const dlls = await fs_readdir(
    path.join(baseDir, "node", mode, "dist", "static", "dll")
  )
  dlls.forEach(item => {
    res.push(`${uri}dll/${item}`)
  })
  $console.warn(`计划刷新${res.length}个文件`)
  const [respBody, respInfo] = await refreshUris(res)
  respInfo.data = respInfo.data.toString()
  if (respInfo.statusCode == 200) {
    const jsonBody = JSON.parse(respBody.toString())
    $console.warn(`
-----*完毕*-----
返回状态码：${jsonBody.code}(${jsonBody.error})
错误地址有：${JSON.stringify(jsonBody.invalidUrls)}
错误路径有：${JSON.stringify(jsonBody.invalidDirs)}
url限额：${jsonBody.urlSurplusDay}/${jsonBody.urlQuotaDay}
dir限额：${jsonBody.dirSurplusDay}/${jsonBody.dirQuotaDay}
    `)
  } else {
    $console.error(
      `七牛刷新失败,因为返回状态码是${
        respInfo.statusCode
      },具体错误信息:${JSON.stringify(respInfo)}`
    )
  }
}
const refreshs = async function(uri) {
  //CDN预取
  $console.warn(`准备刷新七牛CDN,每日名额:10,超过后失败`)
  const [respBody, respInfo] = await refreshUris([uri])
  respInfo.data = respInfo.data.toString()
  if (respInfo.statusCode == 200) {
    const jsonBody = JSON.parse(respBody.toString())
    $console.warn(`
-----*完毕*-----
返回状态码：${jsonBody.code}(${jsonBody.error})
错误地址有：${JSON.stringify(jsonBody.invalidUrls)}
错误路径有：${JSON.stringify(jsonBody.invalidDirs)}
url限额：${jsonBody.urlSurplusDay}/${jsonBody.urlQuotaDay}
dir限额：${jsonBody.dirSurplusDay}/${jsonBody.dirQuotaDay}
    `)
  } else {
    $console.error(
      `七牛刷新失败,因为返回状态码是${
        respInfo.statusCode
      },具体错误信息:${JSON.stringify(respInfo)}`
    )
  }
}
const createHtml = async function(mode, uri, target) {
  $console.warn(`准备生成预设文件`)
  const res = []
  const jss = await fs_readdir(
    path.join(baseDir, "node", mode, "dist", "static", "js")
  )
  jss.forEach(item => {
    res.push(`${uri}js/${item}`)
  })
  const csss = await fs_readdir(
    path.join(baseDir, "node", mode, "dist", "static", "css")
  )
  csss.forEach(item => {
    res.push(`${uri}css/${item}`)
  })
  const dlls = await fs_readdir(
    path.join(baseDir, "node", mode, "dist", "static", "dll")
  )
  dlls.forEach(item => {
    res.push(`${uri}dll/${item}`)
  })

  const url = "https://www.17ce.com/site/checkuser"
  for (let item of res) {
    const response = await rp({
      method: "POST",
      uri: url,
      form: {
        url: item,
        type: "https",
        isp: 0
      },
      headers: {
        "User-Agent":
          "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
        Host: "www.17ce.com",
        Connection: "keep-alive",
        Accept: "*/*",
        Origin: "https://www.17ce.com",
        "X-Requested-With": "XMLHttpRequest",
        "User-Agent":
          "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
        "Content-Type": "application/x-www-form-urlencoded",
        Referer: "https://www.17ce.com/",
        "Accept-Encoding": "deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.9,la;q=0.8,en;q=0.7,id;q=0.6",
        Cookie:
          "PHPSESSID=5qmicpli1p8mrmlos52k5vvi25; __utma=45994468.1976812791.1535273195.1535273195.1535273195.1; __utmc=45994468; __utmz=45994468.1535273195.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; UM_distinctid=1657569d487738-036bbc658081c4-9393265-11cc40-1657569d488487; CNZZDATA3403072=cnzz_eid%3D2115353626-1535270022-https%253A%252F%252Fwww.baidu.com%252F%26ntime%3D1535270022; Hm_lvt_268c7656687d2c5e9f5c12b7108fc7ad=1535273195; __utmt=1; allSites=https%3A%7Cbaidu.com; maxid=567861; __utmb=45994468.6.10.1535273195; Hm_lpvt_268c7656687d2c5e9f5c12b7108fc7ad=1535274538"
      },
      json: true
    })
    if (response.rt === true) {
      $console.warn(`
${item} ----> 成功
覆盖终端: ${response.data.fullips.length}
        `)
    } else {
      $console.error(`${item}失败!`)
    }
  }

  $console.success(`完毕`)
  // const fileName = format(new Date())
  // const fileTar = `${baseDir}${path.sep}${fileName}.html`
  // await fs_writeFile(
  //   fileTar,
  //   res.join('\n'), {
  //     encoding: 'utf-8'
  //   })
  // const targetFile = path.join(target, mobile, "cdn")
  // await mv(fileTar, targetFile)
  // $console.success(`完毕，请使用工具访问: https://kll.emelife.cn/cdn/${fileName}.html`)
}
const build = async function(mode, type = "build", needcp = false) {
  $console.warn(`准备【打包】${mode}`)
  await gotoProject(mode)
  await exec(`yarn ${type}`)
  if (needcp) {
    await mkdir(needcp)
    // 备份html
    const now = new Date()
    await cp(
      path.join(needcp, "dist", "index.html"),
      path.join(needcp, `${format(now)}.html`)
    )
    await cp(path.join(baseDir, "node", mode, "dist"), needcp)
  }
  $console.success(`${mode} 【打包】完成`)
}

const dev = async function(mode) {
  $console.warn(`准备【打包】${mode}`)
  await gotoProject(mode)
  await exec(`yarn dev`)
}
const start = async function() {
  startTime = +new Date()
}
const reset = async function(server) {
  server.bufferedCommand = ""
  server.displayPrompt()
  $console.warn(`耗时${+new Date() - startTime}ms`)
}

const javaBuild = async function(mode) {
  await returnBaseUri()
  let max = new Date()
  await exec(`mvn package -P${mode} -DskipTests`)
  $console.warn("清除多余打包文件开始...")
  await rm(path.join(baseDir, "target", "archive-tmp"))
  await rm(path.join(baseDir, "target", "classes"))
  await rm(path.join(baseDir, "target", "dependency-maven-plugin-markers"))
  await rm(path.join(baseDir, "target", "generated-sources"))
  await rm(path.join(baseDir, "target", "maven-archiver"))
  await rm(path.join(baseDir, "target", "maven-status"))
  await rm(path.join(baseDir, "target", "stage"))
  await rm(path.join(baseDir, "target", "*.jar"))
  await rm(path.join(baseDir, "target", "test-classes"))
  await rm(path.join(baseDir, "target", "jacoco.exec"))
  await rm(path.join(baseDir, "target", "site"))
  await rm(path.join(baseDir, "target", "generated-test-sources"))

  $console.warn("备份历史包开始...")
  const targzs = await fs_readdir(path.join(baseDir, "target", "dist"))
  for (let fi of targzs) {
    if (fi.match(/.tar.gz$/)) {
      const stat = await fs_stat_(path.join(baseDir, "target", "dist", fi))
      const now = stat.mtime
      if (now > max) {
        max = now
      }
      await mv(
        path.join(baseDir, "target", "dist", fi),
        path.join(baseDir, "target", "dist", `${format(now)}.tar.gz`)
      )
    }
  }
  $console.success(`打包完毕,java打包结果是:${format(max)}.gz`)
}

const actions = {
  install: {
    name: "依赖安装",
    avs: []
  },
  env: {
    name: "环境",
    avs: []
  },
  build: {
    name: "打包",
    avs: []
  },
  cdn: {
    name: "cdn",
    avs: []
  }
}
actions.env.avs.push({
  action: "debug",
  help: "开启日志完整模式",
  async fn() {
    await start()
    silent = false
  }
})
actions.install.avs.push({
  action: "install",
  help: "完整安装node-pc项目的依赖",
  async fn() {
    await start()
    await baseInstall(pc, true)
    await reset(this)
  }
})
actions.install.avs.push({
  action: "install-m",
  help: "完整安装node-mobile项目的依赖",
  async fn() {
    await start()
    await baseInstall(mobile)
    await reset(this)
  }
})
actions.install.avs.push({
  action: "install-all",
  help: "完整安装全部node项目的依赖",
  async fn() {
    await start()
    await baseInstall(mobile)
    await baseInstall(pc, true)
    await reset(this)
  }
})
actions.build.avs.push({
  action: "build-test",
  help: "打包测试环境",
  async fn() {
    await start()

    await build(pc, "build", path.join(baseDirTest, package.base.pcDist))
    await build(
      mobile,
      "build:test",
      path.join(baseDirTest, package.base.mobileDist)
    )
    await javaBuild("test")
    await reset(this)
  }
})

actions.build.avs.push({
  action: "build-pro",
  help: "打包生产环境",
  async fn() {
    await start()
    await build(pc, "build", path.join(baseDirPro, package.base.pcDist))
    await build(mobile, "build", path.join(baseDirPro, package.base.mobileDist))
    await javaBuild("pro")
    await reset(this)
  }
})

actions.build.avs.push({
  action: "build-java-pro",
  help: "打包java的生产环境",
  async fn() {
    await start()
    await javaBuild("pro")
    await reset(this)
  }
})
actions.build.avs.push({
  action: "build-java-test",
  help: "打包java的测试环境",
  async fn() {
    await start()
    await javaBuild("test")
    await reset(this)
  }
})

actions.build.avs.push({
  action: "build-vue-pro",
  help: "打包vue的生产环境",
  async fn() {
    await start()
    await build(pc, "build", path.join(baseDirPro, package.base.pcDist))
    await build(mobile, "build", path.join(baseDirPro, package.base.mobileDist))
    await reset(this)
  }
})

actions.build.avs.push({
  action: "build-m-pro",
  help: "打包mobile的生产环境",
  async fn() {
    await start()
    await build(mobile, "build", path.join(baseDirPro, package.base.mobileDist))
    await reset(this)
  }
})

actions.build.avs.push({
  action: "build-p-pro",
  help: "打包pc的生产环境",
  async fn() {
    await start()
    await build(pc, "build", path.join(baseDirPro, package.base.pcDist))
    await reset(this)
  }
})

actions.build.avs.push({
  action: "build-vue-test",
  help: "打包vue的测试环境",
  async fn() {
    await start()
    await build(pc, "build", path.join(baseDirTest, package.base.pcDist))
    await build(
      mobile,
      "build:test",
      path.join(baseDirTest, package.base.mobileDist)
    )
    await reset(this)
  }
})

actions.build.avs.push({
  action: "build-m-test",
  help: "打包mobile的测试环境",
  async fn() {
    await start()
    await build(
      mobile,
      "build:test",
      path.join(baseDirTest, package.base.mobileDist)
    )
    await reset(this)
  }
})

actions.build.avs.push({
  action: "build-p-test",
  help: "打包pc的测试环境",
  async fn() {
    await start()
    await build(pc, "build", path.join(baseDirTest, package.base.pcDist))
    await reset(this)
  }
})

actions.cdn.avs.push({
  action: "fetch-m",
  help: "预取移动端资源到CDN【每日限额100，慎用】",
  async fn() {
    await start()
    await fetch(mobile, baseUriPro)
    await reset(this)
  }
})

actions.cdn.avs.push({
  action: "ref-m",
  help: "刷新移动端CDN每个文件【每日限额500，慎用】",
  async fn() {
    await start()
    await refresh(mobile, baseUriPro)
    await reset(this)
  }
})

actions.cdn.avs.push({
  action: "ref-m-a",
  help: "刷新移动端CDN目录【每日限额10，慎用】",
  async fn() {
    await start()
    await refreshs(baseUriPro)
    await reset(this)
  }
})
actions.cdn.avs.push({
  action: "ref-mh",
  help: "生成一个html文件用于预取CDN",
  async fn() {
    await start()
    await createHtml(mobile, baseUriPro, baseDirPro)
    await reset(this)
  }
})
actions.install.avs.push({
  action: "add",
  help: "替代yarn add",
  async fn(lib) {
    await start()
    await install(pc, lib)
    await baseInstall(pc, true)
    await reset(this)
  }
})
actions.install.avs.push({
  action: "remove",
  help: "替代yarn remove",
  async fn(lib) {
    await start()
    await uninstall(pc, lib)
    await baseInstall(pc, true)
    await reset(this)
  }
})

module.exports = actions
